# -*- coding: utf-8 -*-
# Scikit-learn-1.py
"""
### 实验题目
#### 题目1：线性回归预测糖尿病进展（回归问题）
**任务描述**：  
使用Scikit-learn内置的糖尿病数据集，建立一个线性回归模型来预测糖尿病进展指标。  
**步骤**：  
1. 加载糖尿病数据集（`load_diabetes`）。
2. 将数据集分为训练集和测试集（80%训练，20%测试）。
3. 训练线性回归模型。
4. 在测试集上进行预测。
5. 计算模型在测试集上的均方误差（MSE）和决定系数（R²）。
6. 可视化预测结果：绘制实际值与预测值的散点图，残差分析图，以及特征与目标值关系图。
"""

from matplotlib import pyplot as plt
import numpy as np
from sklearn.datasets import load_diabetes                  # sklearn内置的糖尿病数据集
from sklearn.linear_model import LinearRegression           # 线性回归算法模型
from ScikitLearnBase import ScikitLearnExperiment, setup_chinese_font  # 导入实验基类和字体设置

# 确保字体设置正确
setup_chinese_font()

# 创建实验实例
experiment = ScikitLearnExperiment("线性回归预测糖尿病进展")

# 打印实验标题
experiment.print_header()

# 1. 加载数据
print("1. 加载糖尿病数据集...")
diabetes = experiment.load_dataset(load_diabetes)

# 特征名称中英文对照
feature_name_mapping = {
    'age': '年龄',
    'sex': '性别',
    'bmi': '身体质量指数',
    'bp': '平均血压',
    's1': '血清总胆固醇',
    's2': '低密度脂蛋白',
    's3': '高密度脂蛋白',
    's4': '总胆固醇/高密度脂蛋白',
    's5': '血清甘油三酯对数',
    's6': '血糖水平'
}

# 创建中文特征名称列表
chinese_feature_names = experiment.create_feature_name_mapping(feature_name_mapping)
print(f"特征名称(中文): {', '.join(chinese_feature_names)}")

# 2. 划分训练集和测试集
experiment.split_data(test_size=0.2, random_state=0)

# 3. 创建并训练模型
print("3. 训练线性回归模型中...")
reg = LinearRegression()
reg.fit(experiment.X_train, experiment.y_train)
print("训练完成！")

# 4. 预测
y_pred = reg.predict(experiment.X_test)

# 5. 评估模型
mse, r2 = experiment.evaluate_model(reg, experiment.X_test, experiment.y_test, "线性回归模型")
print(f"模型系数 (coef_): {reg.coef_}")
print(f"模型截距 (intercept_): {reg.intercept_}")

# 打印特征重要性（使用中英文对照）
print("\n特征重要性:")
for i, (en_name, cn_name, coef) in enumerate(zip(experiment.feature_names, chinese_feature_names, reg.coef_)):
    print(f"{en_name}({cn_name}): {coef:.4f}")

# 输出特征重要性排序（使用中文名称）
print("\n特征重要性排序（按绝对值降序）:")
sorted_indices = np.argsort(np.abs(reg.coef_))[::-1] # 按系数绝对值降序排列
for i, idx in enumerate(sorted_indices):
    print(f"{i+1}. {chinese_feature_names[idx]}({experiment.feature_names[idx]}): {reg.coef_[idx]:.4f}")

# 6. 可视化预测结果
plt.figure(figsize=(15, 10))

# 1. 实际值 vs 预测值散点图
plt.subplot(2, 2, 1)
experiment.plot_scatter(experiment.y_test, y_pred, 
                       "糖尿病进展预测: 实际值 vs 预测值", 
                       "实际值", "预测值", alpha=0.5)
plt.plot([min(experiment.y_test), max(experiment.y_test)], 
         [min(experiment.y_test), max(experiment.y_test)], 'r--', lw=2)

# 使用R^2而不是R²避免特殊字符问题
plt.text(0.05, 0.95, f"R^2 = {r2:.4f}\nMSE = {mse:.4f}", 
         transform=plt.gca().transAxes, 
         verticalalignment='top',
         bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))

# 2. 残差图分析
plt.subplot(2, 2, 2)
residuals = experiment.y_test - y_pred
experiment.plot_scatter(y_pred, residuals, 
                       "残差分析图", 
                       "预测值", "残差 (实际值 - 预测值)", alpha=0.5)
plt.axhline(y=0, color='r', linestyle='-')

# 3. 特征与目标值关系图 (选取两个最重要特征)
top_features = np.argsort(np.abs(reg.coef_))[-2:]
for i, feature_idx in enumerate(top_features):
    plt.subplot(2, 2, 3 + i)
    experiment.plot_scatter(experiment.X_test[:, feature_idx], experiment.y_test, 
                           f"{chinese_feature_names[feature_idx]}与目标值关系",
                           chinese_feature_names[feature_idx], "目标值", 
                           alpha=0.5, label='实际值')
    experiment.plot_scatter(experiment.X_test[:, feature_idx], y_pred, 
                           f"{chinese_feature_names[feature_idx]}与目标值关系",
                           chinese_feature_names[feature_idx], "目标值", 
                           alpha=0.5, label='预测值')
    plt.legend()

plt.tight_layout()

# 保存结果为png图片
experiment.save_plot("diabetes_prediction_results.png")

# 显示可视化结果
plt.show()

# 打印实验结束信息
experiment.print_footer()